/**
 * $version:  0.1 
 * $Date: 2012-04-05 
 *
 * Copyright (C) 2010-2012 Jawa Software. All rights reserved.
 *
 */

package org.jawa.core;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.DateConverter;
import org.jawa.core.cache.CacheFactory;
import org.jawa.core.cluster.ClusterManager;
import org.jawa.core.container.PluginManager;
import org.jawa.core.database.DbConnectionManager;
import org.jawa.core.exception.InitializationException;
import org.jawa.core.log.Log;
import org.jawa.core.util.JawaGlobals;

public class JawaServer implements Serializable {

    private static final long serialVersionUID = 4875281305713873479L;

    // 服务器实例
    private static JawaServer instance;

    // 服务器名称
    private String name;

    // 服务器IP地址
    private String host;

    // 服务器版本
    private Version version;

    // 服务器最后一次启动时间
    private Date startDate;

    // 服务器是否初始化
    private boolean initialized = false;

    // 服务器是否已经启动
    private boolean started = false;

    // 是否已安装.
    private boolean setupMode = true;

    // 服务器信息.
    private JawaServerInfo serverInfo;

    // 服务器在集群中的唯一标识
    private String memberName;

    // 服务器工作目录
    private File jawaHome;

    // 服务器的 Class Loader
    private ClassLoader loader;

    // 在服务器启动或停止的时候发布事件.
    private List<JawaServerListener> listeners = new CopyOnWriteArrayList<JawaServerListener>();

    // 插件管理器.
    private PluginManager pluginManager;

    /**
     * 返回服务器的单一实例.
     * 
     * @return 服务器的单一实例.
     */
    public static JawaServer getInstance() {
	return instance;
    }

    /**
     * 创建服务器并启动.
     */
    public JawaServer() {
	if (instance != null) {
	    throw new IllegalStateException("服务器已经启动");
	}

	instance = this;

	// 启动服务器
	start();
    }

    /**
     * 初始化服务器.
     * 
     * @throws FileNotFoundException
     */
    private void initialize() throws FileNotFoundException {

	// 确定服务器工作目录
	locateJawaHome();

	name = JawaGlobals.getXMLProperty("server.name", "JAWA").toLowerCase();

	try {
	    host = InetAddress.getLocalHost().getHostName();
	} catch (UnknownHostException ex) {
	    Log.warn("无法确定本地主机名.", ex);
	}

	version = new Version(1, 0, 0, Version.ReleaseStatus.Alpha);

	if ("true".equals(JawaGlobals.getXMLProperty("setup"))) {
	    setupMode = false;
	}

	loader = Thread.currentThread().getContextClassLoader();

	// 初始化本地缓存
	try {
	    CacheFactory.initialize();
	} catch (InitializationException e) {
	    Log.error(e.getMessage(), e);
	}

	// 尝试启动集群缓存
	if (ClusterManager.isClusteringEnabled()) {
	    ClusterManager.setClusteringEnabled(true);
	}

	DateConverter dateConverter = new DateConverter();
	dateConverter.setPattern("yyyy-MM-dd");
	ConvertUtils.register(dateConverter, java.util.Date.class);

	initialized = true;
    }

    /**
     * 完成安装过程.
     */
    public void finishSetup() {
	if (!setupMode) {
	    return;
	}
	if ("true".equals(JawaGlobals.getXMLProperty("setup"))) {
	    setupMode = false;
	}
    }

    /**
     * 启动服务器.
     */
    public void start() {
	try {
	    initialize();

	    startDate = new Date();

	    serverInfo = new JawaServerInfo(name, host, version, startDate);

	    // 初始化插件管理器,并不启动
	    File pluginDir = new File(jawaHome, "plugins");
	    pluginManager = new PluginManager(pluginDir);

	    // 如果已正确安装
	    if (!setupMode) {
		verifyDataSource();
	    }

	    // 启动插件管理器
	    pluginManager.start();

	    String startupBanner = "[服务器]  版本: " + version.getVersionString()
		    + " ( " + new Date() + " )";
	    System.out.println(startupBanner);

	    started = true;

	} catch (Exception e) {
	    Log.error(e);
	    System.out.println(("服务器启动失败"));
	}
    }

    /**
     * 返回服务器主机名.
     * 
     * @return
     */
    public String getHostName() {
	return host;
    }

    /**
     * 校验服务器工作目录的正确性.
     * 
     * @param homeGuess
     *            服务器工作目录.
     * @param jawaConfigName
     *            服务器配置文件.
     * @return 服务器工作目录.
     * @throws java.io.FileNotFoundException
     *             服务器工作目录没有找到
     */
    private File verifyHome(String homeGuess, String jawaConfigName)
	    throws FileNotFoundException {
	File jawaHome = new File(homeGuess);
	File configFile = new File(jawaHome, jawaConfigName);
	if (!configFile.exists()) {
	    throw new FileNotFoundException();
	} else {
	    try {
		return new File(jawaHome.getCanonicalPath());
	    } catch (Exception ex) {
		throw new FileNotFoundException();
	    }
	}
    }

    /**
     * <p>
     * 返回当前服务器工作的目录.
     * </p>
     * 
     * @throws FileNotFoundException
     *             无法确定服务器工作的目录.
     */
    private void locateJawaHome() throws FileNotFoundException {

	String jawaConfigName = "conf" + File.separator + "jawa.xml";

	if (JawaGlobals.getHomeDirectory() != null) {
	    jawaConfigName = JawaGlobals.getConfigName();
	    jawaHome = verifyHome(JawaGlobals.getHomeDirectory(),
		    jawaConfigName);
	}
	if (jawaHome == null) {
	    String homeProperty = System.getProperty("jawaHome");
	    try {
		if (homeProperty != null) {
		    jawaHome = verifyHome(homeProperty, jawaConfigName);
		}
	    } catch (FileNotFoundException fe) {
		// 忽略.
	    }
	}

	if (jawaHome == null) {
	    try {
		jawaHome = verifyHome("..", jawaConfigName).getCanonicalFile();
	    } catch (FileNotFoundException fe) {
		// 忽略.
	    } catch (IOException ie) {
		// 忽略.
	    }
	}

	if (jawaHome == null) {
	    System.err.println("无法确定当前服务器的工作目录");
	    throw new FileNotFoundException();
	} else {
	    JawaGlobals.setHomeDirectory(jawaHome.toString());
	    JawaGlobals.setConfigName(jawaConfigName);
	}
    }

    /**
     * 验证数据库连接情况.
     */
    private void verifyDataSource() {
	java.sql.Connection conn = null;
	try {
	    conn = DbConnectionManager.getConnection();
	    PreparedStatement stmt = conn
		    .prepareStatement("SELECT count(*) FROM app_id");
	    ResultSet rs = stmt.executeQuery();
	    rs.next();
	    rs.close();
	    stmt.close();
	} catch (Exception e) {
	    System.err.println("数据库安装或配置错误: " + "请检查数据库的配置是否正确,在 "
		    + "logs/error.log 文件中记录了详细的错误情况.");
	    Log.error("数据库无法正常访问", e);
	    throw new IllegalArgumentException(e);
	} finally {
	    if (conn != null) {
		try {
		    conn.close();
		} catch (SQLException e) {
		    Log.error(e);
		}
	    }
	}
    }

    /**
     * 返回该服务器在集群中的唯一标识.
     * 
     * @return 服务器在集群中的唯一标识.
     */
    public String getMemberName() {
	return memberName == null ? getHostName() : memberName;
    }

    /**
     * 设置该服务器在集群中的唯一标识
     * 
     * @param 该服务器在集群中的唯一标识
     *            .
     */
    public void setMemberName(String memberName) {
	this.memberName = memberName;
    }

    /**
     * 服务器是否已正确安装.
     * 
     * @return 服务器是否已正确安装.
     */
    public boolean isSetupMode() {
	return setupMode;
    }

    /**
     * 服务器是否已经开始启动.
     * 
     * @return 服务器是否已经开始启动.
     */
    public boolean isStarted() {
	return started;
    }

    /**
     * 返回服务器信息.
     * 
     * @return 服务器信息
     */
    public JawaServerInfo getServerInfo() {
	if (!initialized) {
	    throw new IllegalStateException("服务器没有初始化!");
	}
	return serverInfo;
    }

    /**
     * 增加服务器事件监听.
     * 
     * @param jawaServerListener
     */
    public void addServerListener(JawaServerListener jawaServerListener) {
	listeners.add(jawaServerListener);
    }

    /**
     * 删除服务器事件监听.
     * 
     * @param 服务器事件
     *            .
     */
    public void removeServerListener(JawaServerListener listener) {
	listeners.remove(listener);
    }

    /**
     * 服务器停止.
     */
    public void stop() {
	ClusterManager.shutdown();

	// 停止所有插件
	if (pluginManager != null) {
	    pluginManager.shutdown();
	}

	DbConnectionManager.destroyConnectionProvider();
	Log.info("服务器停止!");

    }

    /**
     * Returns the <code>PluginManager</code> instance registered with this
     * server.
     * 
     * @return the PluginManager instance.
     */
    public PluginManager getPluginManager() {
	return pluginManager;
    }

    /**
     * 返回服务器版本.
     * 
     * @return 服务器版本
     */
    public Version getVersion() {
	return version;
    }

}
